Imports System.Drawing

Namespace Overlay
    Public MustInherit Class OverLayBase
        Protected moChartDimensions As ChartDimensions
        Protected moXAxis As XAxis
        Protected moYAxis As YAxis
        Protected moSeries As New ArrayList

        Protected moRegionList As ArrayList

        Public Sub New( _
            ByVal oChartDimensions As ChartDimensions, _
            ByVal oXAxis As XAxis, _
            ByVal oYAxis As YAxis)

            moChartDimensions = oChartDimensions
            moXAxis = oXAxis
            moYAxis = oYAxis

            moRegionList = New ArrayList
        End Sub

        ''' <summary>
        ''' An ArrayList containing series. 
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Series() As ArrayList
            Get
                Return moSeries
            End Get
            Set(ByVal value As ArrayList)
                moSeries = value
            End Set
        End Property

        ''' <summary>
        ''' Draws all charts belonging to the OverLay on a System.Drawing.Graphics object.
        ''' </summary>
        ''' <param name="g"></param>
        ''' <remarks></remarks>
        Public Sub DrawChart(ByRef g As Graphics)
            DrawAllSeries(g)
        End Sub

        Protected MustOverride Sub DrawAllSeries(ByRef g As Graphics)

        ''' <summary>
        ''' An ArrayList containing jChart.RegionArea objects for each point described by any series belonging to the OverLay object.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property RegionList() As ArrayList
            Get
                Return moRegionList
            End Get
            Set(ByVal value As ArrayList)
                moRegionList = value
            End Set
        End Property

        Protected Sub AddLineRegion(ByVal oRegion As RegionArea)
            moRegionList.Add(oRegion)
        End Sub

        Protected Sub AddPolygonRegion(ByVal oRegion As RegionArea)
            moRegionList.Add(oRegion)
        End Sub

        Protected Sub GetLineCoordinates_Entering( _
                        ByVal snNullValue As Single, _
                        ByVal snXStart As Single, _
                        ByVal snYStart As Single, _
                        ByVal snXEnd As Single, _
                        ByVal snYEnd As Single, _
                        ByRef oStartPos As Point, _
                        ByRef oEndPos As Point)

            If snYStart = snNullValue Then
                snYStart = snYEnd

            ElseIf snYEnd = snNullValue Then
                snYEnd = snYStart

            End If

            Dim iXStart As Integer = moXAxis.CalculateXPos(CInt(snXStart))
            Dim iYStart As Integer = moYAxis.CalculateYPos(snYStart)

            Dim iXEnd As Integer = moXAxis.CalculateXPos(CInt(snXEnd))
            Dim iYEnd As Integer = moYAxis.CalculateYPos(snYEnd)

            '---Now find midpoint of line
            Dim snSlope = (iYStart - iYEnd) / (iXStart - iXEnd)

            Dim iXMidPoint As Integer = iXStart + (iXEnd - iXStart) / 2
            Dim iYMidPoint As Integer = iYStart + ((iXEnd - iXStart) / 2) * snSlope

            oStartPos.X = iXStart
            oStartPos.Y = iYStart

            oEndPos.X = iXMidPoint
            oEndPos.Y = iYMidPoint
        End Sub

        Protected Sub GetLineCoordinates_Exiting( _
                        ByVal snNullValue As Single, _
                        ByVal snXStart As Single, _
                        ByVal snYStart As Single, _
                        ByVal snXEnd As Single, _
                        ByVal snYEnd As Single, _
                        ByRef oStartPos As Point, _
                        ByRef oEndPos As Point)

            If snYStart = snNullValue Then
                snYStart = snYEnd

            ElseIf snYEnd = snNullValue Then
                snYEnd = snYStart

            End If

            Dim iXStart As Integer = moXAxis.CalculateXPos(CInt(snXStart))
            Dim iYStart As Integer = moYAxis.CalculateYPos(snYStart)

            Dim iXEnd As Integer = moXAxis.CalculateXPos(CInt(snXEnd))
            Dim iYEnd As Integer = moYAxis.CalculateYPos(snYEnd)

            '---Now find midpoint of line
            Dim snSlope = (iYStart - iYEnd) / (iXStart - iXEnd)

            Dim iXMidPoint As Integer = iXStart + (iXEnd - iXStart) / 2
            Dim iYMidPoint = iYStart + ((iXEnd - iXStart) / 2) * snSlope

            oStartPos.X = iXMidPoint
            oStartPos.Y = iYMidPoint

            oEndPos.X = iXEnd
            oEndPos.Y = iYEnd
        End Sub

        Protected Function CalculateBarAreaRegions( _
                 ByRef g As Graphics, _
                 ByVal sSeriesName As String, _
                 ByVal oTopValues() As Single, _
                 ByVal oBottomValues() As Single, _
                 ByVal iBarWidth As Integer, _
                 ByVal iBarOffset As Integer) As ArrayList

            Dim oRegionList As New ArrayList

            Dim iXPointCount As Integer = moXAxis.XAxisValues.GetUpperBound(0)

            '--OK, we now have a list of values. Let's them into a bar
            For iXPos As Integer = 0 To iXPointCount
                Dim oPolygonPoints(3) As Point

                Dim snYValue As Single
                Dim iAdjustedYPos As Integer
                Dim iAdjustedXPos As Integer

                '---Top of new series
                snYValue = oTopValues(iXPos)

                iAdjustedYPos = moYAxis.CalculateYPos(CSng(snYValue))
                iAdjustedXPos = moXAxis.CalculateXPos(iXPos) + iBarOffset

                '--Upper-left
                With oPolygonPoints(0)
                    .X = iAdjustedXPos - (iBarWidth / 2)
                    .Y = iAdjustedYPos
                End With

                '--Upper-right
                With oPolygonPoints(1)
                    .X = iAdjustedXPos + (iBarWidth / 2)
                    .Y = iAdjustedYPos
                End With

                '---Top of old series
                snYValue = oBottomValues(iXPos)
                iAdjustedYPos = moYAxis.CalculateYPos(snYValue)
                iAdjustedXPos = moXAxis.CalculateXPos(iXPos) + iBarOffset

                '---Lower-right
                With oPolygonPoints(2)
                    .X = iAdjustedXPos + (iBarWidth / 2)
                    .Y = iAdjustedYPos
                End With

                '---Lower-left
                With oPolygonPoints(3)
                    .X = iAdjustedXPos - (iBarWidth / 2)
                    .Y = iAdjustedYPos
                End With

                Dim oRegion As New RegionArea( _
                                        sSeriesName:=sSeriesName, _
                                        snxvalue:=iXPos, _
                                        snYValue:=oTopValues(iXPos) - oBottomValues(iXPos), _
                                        oseriespoints:=oPolygonPoints)

                oRegionList.Add(oRegion)
            Next

            Return oRegionList
        End Function

        Protected Function CalculateAreaRegions( _
                     ByRef g As Graphics, _
                     ByVal oSeries As Object, _
                     ByVal oTopValues() As Single, _
                     ByVal oBottomValues() As Single) As ArrayList

            Dim oRegionList As New ArrayList

            Dim iXPointCount As Integer = moXAxis.XAxisValues.GetUpperBound(0)

            Dim bIsEnd As Boolean = False
            Dim bIsMiddle As Boolean = False
            Dim bIsStart As Boolean = False

            '--OK, we now have a list of values. Let's them into polygons
            For iXPos As Integer = 0 To iXPointCount
                bIsEnd = False
                bIsMiddle = False
                bIsStart = False

                If oTopValues(iXPos) <> oSeries.NullValue Then
                    If iXPos = 0 Then
                        bIsStart = True

                    ElseIf iXPos > 0 AndAlso _
                        iXPos < iXPointCount AndAlso _
                        oTopValues(iXPos - 1) = oSeries.NullValue Then

                        bIsMiddle = True

                    ElseIf iXPos < iXPointCount AndAlso _
                            oTopValues(iXPos + 1) = oSeries.NullValue Then

                        bIsMiddle = True

                    ElseIf iXPos = iXPointCount AndAlso _
                           iXPos > 0 AndAlso _
                           oTopValues(iXPos - 1) = oSeries.NullValue Then

                        bIsEnd = True

                    ElseIf iXPos = iXPointCount Then
                        bIsEnd = True

                    Else
                        bIsMiddle = True
                    End If
                End If

                    '---4 points in each end, six points in each middle
                    If bIsStart Then
                        '---Beginning of area
                        Dim oPolygonPoints(3) As Point

                        GetLineCoordinates_Entering( _
                                    oSeries.NullValue, _
                                    iXPos, _
                                    oTopValues(iXPos), _
                                    iXPos + 1, _
                                    oTopValues(iXPos + 1), _
                                    oPolygonPoints(0), _
                                    oPolygonPoints(1))

                        GetLineCoordinates_Entering( _
                                    oSeries.NullValue, _
                                    iXPos, _
                                    oBottomValues(iXPos), _
                                    iXPos + 1, _
                                    oBottomValues(iXPos + 1), _
                                    oPolygonPoints(3), _
                                    oPolygonPoints(2))

                        Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                        oRegionList.Add(oRegion)

                End If

                If bIsEnd Then
                    '---End of area
                    Dim oPolygonPoints(3) As Point

                    GetLineCoordinates_Exiting( _
                                oSeries.NullValue, _
                                iXPos - 1, _
                                oTopValues(iXPos - 1), _
                                iXPos, _
                                oTopValues(iXPos), _
                                oPolygonPoints(0), _
                                oPolygonPoints(1))

                    GetLineCoordinates_Exiting( _
                                oSeries.NullValue, _
                                iXPos - 1, _
                                oBottomValues(iXPos - 1), _
                                iXPos, _
                                oBottomValues(iXPos), _
                                oPolygonPoints(3), _
                                oPolygonPoints(2))

                    Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                    oRegionList.Add(oRegion)

                End If

                If bIsMiddle Then
                    '---Middle of area
                    Dim oPolygonPoints(5) As Point
                    Dim oDummy As New Point

                    GetLineCoordinates_Entering( _
                                oSeries.NullValue, _
                                iXPos, _
                                oTopValues(iXPos), _
                                iXPos + 1, _
                                oTopValues(iXPos + 1), _
                                oPolygonPoints(0), _
                                oPolygonPoints(1))

                    GetLineCoordinates_Entering( _
                                oSeries.NullValue, _
                                iXPos, _
                                oBottomValues(iXPos), _
                                iXPos + 1, _
                                oBottomValues(iXPos + 1), _
                                oPolygonPoints(3), _
                                oPolygonPoints(2))

                    GetLineCoordinates_Exiting( _
                                oSeries.NullValue, _
                                iXPos - 1, _
                                oTopValues(iXPos - 1), _
                                iXPos, _
                                oTopValues(iXPos), _
                                oPolygonPoints(5), _
                                oDummy)

                    GetLineCoordinates_Exiting( _
                                oSeries.NullValue, _
                                iXPos - 1, _
                                oBottomValues(iXPos - 1), _
                                iXPos, _
                                oBottomValues(iXPos), _
                                oPolygonPoints(4), _
                                oDummy)

                    Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                    oRegionList.Add(oRegion)
                End If
            Next

            Return oRegionList
        End Function

        Protected Function CalculateLineRegions( _
             ByRef g As Graphics, _
             ByVal oSeries As Series.LineSeries, _
             ByVal oTopValues() As Single, _
             ByVal iRegionThickness As Integer) As ArrayList

            Dim oRegionList As New ArrayList

            Dim iXPointCount As Integer = moXAxis.XAxisValues.GetUpperBound(0)

            If iXPointCount > 0 Then
                '--OK, we now have a list of values. Let's them into polygons
                For iXPos As Integer = 0 To iXPointCount
                    '---4 points in each end, six points in each middle
                    If iXPos = 0 Then
                        '---Beginning of area
                        Dim oPolygonPoints(3) As Point

                        GetLineCoordinates_Entering( _
                                    oSeries.NullValue, _
                                    iXPos, _
                                    oTopValues(iXPos), _
                                    iXPos + 1, _
                                    oTopValues(iXPos + 1), _
                                    oPolygonPoints(0), _
                                    oPolygonPoints(1))


                        oPolygonPoints(0).Y = oPolygonPoints(0).Y - (iRegionThickness / 2)
                        oPolygonPoints(1).Y = oPolygonPoints(1).Y - (iRegionThickness / 2)

                        oPolygonPoints(3).X = oPolygonPoints(0).X
                        oPolygonPoints(3).Y = oPolygonPoints(0).Y + iRegionThickness

                        oPolygonPoints(2).X = oPolygonPoints(1).X
                        oPolygonPoints(2).Y = oPolygonPoints(1).Y + iRegionThickness

                        Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                        oRegionList.Add(oRegion)

                        'g.FillRegion(Brushes.Red, oRegion.SeriesRegion)

                    ElseIf iXPos = iXPointCount Then
                        '---End of area
                        Dim oPolygonPoints(3) As Point

                        GetLineCoordinates_Exiting( _
                                    oSeries.NullValue, _
                                    iXPos - 1, _
                                    oTopValues(iXPos - 1), _
                                    iXPos, _
                                    oTopValues(iXPos), _
                                    oPolygonPoints(0), _
                                    oPolygonPoints(1))

                        oPolygonPoints(0).Y = oPolygonPoints(0).Y - (iRegionThickness / 2)
                        oPolygonPoints(1).Y = oPolygonPoints(1).Y - (iRegionThickness / 2)

                        oPolygonPoints(3).X = oPolygonPoints(0).X
                        oPolygonPoints(3).Y = oPolygonPoints(0).Y + iRegionThickness

                        oPolygonPoints(2).X = oPolygonPoints(1).X
                        oPolygonPoints(2).Y = oPolygonPoints(1).Y + iRegionThickness

                        Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                        oRegionList.Add(oRegion)

                        'g.FillRegion(Brushes.Blue, oRegion.SeriesRegion)

                    Else
                        '---Middle of area
                        Dim oPolygonPoints(5) As Point
                        Dim oDummy As New Point

                        GetLineCoordinates_Entering( _
                                    oSeries.NullValue, _
                                    iXPos, _
                                    oTopValues(iXPos), _
                                    iXPos + 1, _
                                    oTopValues(iXPos + 1), _
                                    oPolygonPoints(0), _
                                    oPolygonPoints(1))

                        oPolygonPoints(0).Y = oPolygonPoints(0).Y - (iRegionThickness / 2)
                        oPolygonPoints(1).Y = oPolygonPoints(1).Y - (iRegionThickness / 2)

                        oPolygonPoints(3).X = oPolygonPoints(0).X
                        oPolygonPoints(3).Y = oPolygonPoints(0).Y + iRegionThickness

                        oPolygonPoints(2).X = oPolygonPoints(1).X
                        oPolygonPoints(2).Y = oPolygonPoints(1).Y + iRegionThickness

                        GetLineCoordinates_Exiting( _
                                    oSeries.NullValue, _
                                    iXPos - 1, _
                                    oTopValues(iXPos - 1), _
                                    iXPos, _
                                    oTopValues(iXPos), _
                                    oPolygonPoints(5), _
                                    oDummy)

                        oPolygonPoints(5).Y = oPolygonPoints(5).Y - (iRegionThickness / 2)
                        oPolygonPoints(4).X = oPolygonPoints(5).X
                        oPolygonPoints(4).Y = oPolygonPoints(5).Y + iRegionThickness

                        Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                        oRegionList.Add(oRegion)

                        'g.FillRegion(Brushes.DeepPink, oRegion.SeriesRegion)
                    End If
                Next
            End If

            Return oRegionList
        End Function

        Protected Function CalculatePointRegions( _
             ByRef g As Graphics, _
             ByVal oSeries As Series.BarSeries, _
             ByVal oTopValues() As Single, _
             ByVal iPointRadius As Integer) As ArrayList

            Dim oRegionList As New ArrayList

            Dim iXPointCount As Integer = moXAxis.XAxisValues.GetUpperBound(0)

            '--OK, we now have a list of values. Let's them into a bar
            For iXPos As Integer = 0 To iXPointCount
                Dim oPolygonPoints(3) As Point

                Dim snYValue As Single
                Dim iAdjustedYPos As Integer
                Dim iAdjustedXPos As Integer

                '---Top of new series
                snYValue = oTopValues(iXPos)

                iAdjustedYPos = moYAxis.CalculateYPos(snYValue)
                iAdjustedXPos = moXAxis.CalculateXPos(iXPos)

                '--Upper-left
                With oPolygonPoints(0)
                    .X = iAdjustedXPos - (iPointRadius / 2)
                    .Y = iAdjustedYPos - (iPointRadius / 2)
                End With

                '--Upper-right
                With oPolygonPoints(1)
                    .X = iAdjustedXPos + (iPointRadius / 2)
                    .Y = iAdjustedYPos - (iPointRadius / 2)
                End With

                '---Lower-right
                With oPolygonPoints(2)
                    .X = iAdjustedXPos + (iPointRadius / 2)
                    .Y = iAdjustedYPos + (iPointRadius / 2)
                End With

                '---Lower-left
                With oPolygonPoints(3)
                    .X = iAdjustedXPos - (iPointRadius / 2)
                    .Y = iAdjustedYPos + (iPointRadius / 2)
                End With

                Dim oRegion As New RegionArea(oSeries.Name, iXPos, oTopValues(iXPos), oPolygonPoints)
                oRegionList.Add(oRegion)
            Next

            Return oRegionList
        End Function

        Protected Function TransparentColor(ByVal oColor As Color, ByVal iAlpha As Integer) As Color
            Return Color.FromArgb(iAlpha, oColor.R, oColor.G, oColor.B)
        End Function
    End Class
End Namespace